package org.raymond.icloud.common.core.util.http;

import cn.hutool.core.net.URLDecoder;
import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.Method;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.raymond.icloud.common.core.constants.CommonConstants;
import org.raymond.icloud.common.core.constants.ErrorCode;
import org.raymond.icloud.common.core.exception.BaseException;
import org.raymond.icloud.common.core.util.FilterUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

public class CachedDecryptRequestWrapper extends HttpServletRequestWrapper {
    private Logger log = LoggerFactory.getLogger(CachedDecryptRequestWrapper.class);
    private AES aes;
    private byte[] body;
    private Map<String, String[]> paramsMap = new LinkedHashMap<>();
    private String queryString = "";

    public CachedDecryptRequestWrapper(HttpServletRequest request, AES aes) {
        super(request);
        this.aes = aes;
        String method = request.getMethod();
        if(StringUtils.equalsIgnoreCase(method, Method.POST.name())) {
            resetBody(request);
        }
        if(StringUtils.equalsIgnoreCase(method, Method.GET.name())) {
            resetParams(request);
        }
    }

    public byte[] getContentAsByteArray() {
        return this.body;
    }

    private void resetBody(HttpServletRequest request)  {
        String requestString = FilterUtil.getRequestString(request);
        if(StringUtils.isBlank(requestString)){
            throw new BaseException(ErrorCode.BAD_REQUEST, "Request body is blank.");
        }
        log.debug("requestString:{}", requestString);
        String requestDecrypted = aes.decryptStr(requestString);
        requestDecrypted = requestDecrypted.replaceAll("\u00a0", " ");
        requestDecrypted = requestDecrypted.replaceAll("\\s+", " ");
        log.debug("requestDecrypted:{}", requestDecrypted);
        body = requestDecrypted.getBytes(CharsetUtil.CHARSET_UTF_8);
    }

    private void resetParams(HttpServletRequest request){
        String requestString = FilterUtil.getRequestParams(request);
        if(StringUtils.isBlank(requestString)){
            return;
        }
        String[] urlParams = requestString.split("=");
        if(urlParams.length!=2 || StringUtils.isBlank(urlParams[1])){
            throw new BaseException(ErrorCode.BAD_REQUEST, String.format("Params template: %s", CommonConstants.PARAMS_TEMPLATE));
        }
        requestString = aes.decryptStr(urlParams[1]);
        requestString = URLDecoder.decode(requestString, StandardCharsets.UTF_8);
        log.debug("requestString:{}", requestString);
        JSONObject json = JSONObject.parseObject(requestString);
        for(String key : json.keySet()){
            Object obj = json.get(key);
            if(obj==null) continue;

            if(obj instanceof Collection){
                Collection<?> col = (Collection<?>) obj;
                String[] vals = col.stream().filter(Objects::nonNull).map(Object::toString).toArray(String[]::new);
                paramsMap.put(key, vals);
            }else if(obj.getClass().isArray()){
                int size = Array.getLength(obj);
                List<String> list = new ArrayList<>();
                for(int i=0; i<size; i++){
                    Object e = Array.get(obj, i);
                    if(e==null) continue;
                    list.add(e.toString());
                }
                paramsMap.put(key, list.toArray(new String[0]));
            }else{
                paramsMap.put(key, new String[]{obj.toString()});
            }
        }
        UrlQuery urlQuery = UrlQuery.of(paramsMap);
        this.queryString = urlQuery.build(StandardCharsets.UTF_8);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream(){
            @Override
            public int read() throws IOException {
                return bais.read();
            }
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }

    public void addParameter(String name,String value) {
        String[] parameterValues = getParameterValues(name);
        if (parameterValues == null) {
            this.paramsMap.put(name, new String[] {value});
        }
    }

    @Override
    public Enumeration<String> getParameterNames() {
        Vector<String> vector = new Vector<String>(paramsMap.keySet());
        return vector.elements();
    }

    @Override
    public String getParameter(String name) {
        if(paramsMap.containsKey(name)) {
            return paramsMap.get(name).length > 0 ? paramsMap.get(name)[0] : "";
        }
        return "";
    }
    @Override
    public String[] getParameterValues(String name) {
        return paramsMap.get(name);
    }
    @Override
    public Map<String, String[]> getParameterMap() {
        return Collections.unmodifiableMap(paramsMap);
    }

    @Override
    public String getQueryString(){
        return StringUtils.isNotBlank(this.queryString)?this.queryString:"";
    }
}
